package com.service.demo;

import org.snmp4j.*;
import org.snmp4j.mp.*;
import org.snmp4j.security.*;
import org.snmp4j.smi.*;
import org.snmp4j.transport.DefaultTcpTransportMapping;
import org.snmp4j.transport.DefaultUdpTransportMapping;
import org.snmp4j.util.MultiThreadedMessageDispatcher;
import org.snmp4j.util.ThreadPool;

import java.io.IOException;
import java.util.Vector;

public class SnmpServer {
    //通常是 "udp:0.0.0.0/1620" 表示监听所有ip通过udp访问1620端口，其中端口可以变
    private String address;

    private String securityName;

    private String authProtocol;

    private String authPassPhrase;

    private String privProtocol;

    private String privPassPhrase;

    public SnmpServer(String address, String securityName, String authProtocol, String authPassPhrase, String privProtocol, String privPassPhrase) throws IOException {
        this.address = address;
        this.securityName = securityName;
        this.authProtocol = authProtocol;
        this.authPassPhrase = authPassPhrase;
        this.privProtocol = privProtocol;
        this.privPassPhrase = privPassPhrase;
        this.init();
    }

    private void init() throws IOException {
        //定义线程池
        ThreadPool threadPool = ThreadPool.create("Trap", 10);
        MultiThreadedMessageDispatcher dispatcher = new MultiThreadedMessageDispatcher(threadPool, new MessageDispatcherImpl());
        //监听地址
        Address listenAddress = GenericAddress.parse(address);
        TransportMapping transport;
        if (listenAddress instanceof UdpAddress) {
            transport = new DefaultUdpTransportMapping((UdpAddress) listenAddress);
        } else {
            transport = new DefaultTcpTransportMapping((TcpAddress) listenAddress);
        }
        dispatcher.addMessageProcessingModel(new MPv1());
        dispatcher.addMessageProcessingModel(new MPv2c());
        dispatcher.addMessageProcessingModel(new MPv3());
        SecurityProtocols.getInstance().addDefaultProtocols();
        Snmp snmp = new Snmp(dispatcher, transport);

        //smmpV3 认证且加密模式
        OID authProtocolOID = null;
        if (authProtocol != null) {
            if (authProtocol.equals("SHA")) {
                authProtocolOID = AuthSHA.ID;
            } else if (authProtocol.endsWith("256")) {
                authProtocolOID = AuthHMAC192SHA256.ID;
            } else if (authProtocol.endsWith("384")) {
                authProtocolOID = AuthHMAC256SHA384.ID;
            } else if (authProtocol.endsWith("512")) {
                authProtocolOID = AuthHMAC384SHA512.ID;
            }
        }
        OID privProtocolOID = null;
        if (privProtocol != null) {
            if (privProtocol.equals("AES-128")) {
                privProtocolOID = PrivAES128.ID;
            } else if (privProtocol.equals("AES-192")) {
                privProtocolOID = PrivAES192.ID;
            } else if (privProtocol.equals("AES-256")) {
                privProtocolOID = PrivAES256.ID;
            }
        }
        USM usm = new USM(SecurityProtocols.getInstance(), new OctetString(MPv3.createLocalEngineID()), 0);
        SecurityModels.getInstance().addSecurityModel(usm);
        snmp.getUSM().addUser(
                new OctetString(securityName),
                new UsmUser(new OctetString(securityName),
                        authProtocolOID,
                        new OctetString(authPassPhrase),
                        privProtocolOID,
                        new OctetString(privPassPhrase)));

        //消息监听器
        snmp.addCommandResponder(new CommandResponder() {

            @Override
            public void processPdu(CommandResponderEvent event) {
                PDU pdu = event.getPDU();
                if (pdu == null) {
                    System.out.println("pdu null");
                    return;
                }
                //OID oid = pdu.get(0).getOid();
                //一个pdu中可能存在多个 数据VariableBinding
                Vector variableBindings = pdu.getVariableBindings();
                for (int i = 0; i < variableBindings.size(); i++) {
                    VariableBinding ele =(VariableBinding) variableBindings.get(i);
                    System.out.printf("oid:%s, value:%s, type:%s%n", ele.getOid(),ele.getVariable().toString(), pdu.getType());
                }

                //下面应该是返回信息给发送方
                StatusInformation statusInformation = new StatusInformation();
                StateReference stateRef = event.getStateReference();
                //你可以把之前的pdu的数据修改一下返回去
                //如果不设置PDU.RESPONSE数据返不回去
                pdu.setType(PDU.RESPONSE);
                //修改了原来的pdu里面的数据
                //pdu.set(0, new VariableBinding(new OID("0.0.0.0"), new OctetString("response:result")));
                //或者清空pdu中的数据，然后让如新数据
                pdu.clear();
                pdu.add(new VariableBinding(new OID("0.0.0.0"), new OctetString("response:success!")));

                //我这里直接就创建一个新的数据体返回去 但是实现不了，好像只能用客户端发送过来的那个pdu
                //PDU resp = new PDU();
                //resp.setType(PDU.RESPONSE);
                //resp.add(new VariableBinding(new OID("0.0.0.0"), new OctetString("response:result")));

                try {
                    event.getMessageDispatcher().returnResponsePdu(event.getMessageProcessingModel(),
                            event.getSecurityModel(), event.getSecurityName(),
                            event.getSecurityLevel(), pdu,
                            event.getMaxSizeResponsePDU(), stateRef, statusInformation);
                } catch (MessageException e) {
                    e.printStackTrace();
                }
            }

        });
        transport.listen();
    }

    public static void main(String[] args) throws IOException {
        new SnmpServer("udp:0.0.0.0/9001", "test", "SHA", "testauth", "AES-128", "testpriv");
    }

}
